<?php


namespace common\service;


use common\model\EntityModel;
use common\model\ModuleModel;
use common\model\ProjectModel;
use common\model\StepCategoryModel;
use common\model\StepWorkflowModel;
use common\model\TaskModel;
use common\model\TaskRelationModel;
use common\validate\Task;
use support\bootstrap\Log;
use support\ErrorCode;
use think\Cache;
use think\module\Module;
use think\Response;
use think\exception\HttpResponseException;
use Webman\Stomp\Client;

class CrontabService
{

    public function __construct()
    {
        $this->entityService = EntityService::getInstance();
    }

    /**
     * 任务逾期检查
     * @return int
     * @throws \Exception|\Throwable
     */
    public function checkTaskStatus()
    {
        $ck = __CLASS__ . __METHOD__;

        if (Cache::get($ck)) {
            throw_strack_exception("already executed task status check", ErrorCode::TASK_STATUS_CHECK_PERFORMED);
        }

        Cache::set($ck, 1, strtotime("tomorrow") - time());
        try {
            $taskService = TaskService::getInstance();
            return $taskService->checkTaskStatus();
        } catch (\Throwable $e) {
            Cache::rm($ck);
            Log::error($e->getMessage());
            Log::error(sprintf("%s:%d", $e->getFile(), $e->getLine()));
            Log::error($e->getTraceAsString());
            // 抛出http异常  通知gocron 执行失败
            throw new HttpResponseException(new Response($e->getMessage(), 500));
        }
    }

    /**
     * 合同过期检查
     * @return int
     * @throws \Exception
     */
    public function checkContractDate()
    {
        $contractService = ContractService::getInstance();
        return $contractService->checkContractDate();
    }

    /**
     * 检查并删除重复生成的任务
     * @param $param
     * @return array
     */
    public function checkRepeatTask($param): array
    {
        $startDate = $param['start_date'] ?? date("Y-m-d H:i:s");
        $endDate = $param['end_date'] ?? date("Y-m-d H:i:s");
        $projectId = $param['project_id'] ?? 0;

        $entityFilter = [
            'created' => ['BETWEEN', [strtotime($startDate), strtotime($endDate)]],
            'is_generate' => 'yes'
        ];
        if ($projectId) {
            $entityFilter['project_id'] = $projectId;
        }
        $entities = model(EntityModel::class)
            ->where($entityFilter)
            ->select();

        $res = [];
        $deleteTaskIdList = [];
        $deleteTaskRelationIdList = [];
        foreach ($entities as $entity) {

            $tasks = model(TaskModel::class)
                ->where([
                    'entity_id' => $entity['id'],
                    'step_type' => 'step_category',
                ])
                ->order('step_category_id ASC')
                ->select();

            // 通过工序分类任务进行合并
            $tasksGroupByStepCategory = array_group_by($tasks, 'step_category_id');
            unset($tasks);
            foreach ($tasksGroupByStepCategory as $tasks) {
                if (count($tasks) > 1) {
                    $res['check_repeat_step_category_task_info'] = $entity['name'] . '-' . $tasks[0]['name'] . 'has repeat task';
                    $notAssignedStepCategoryTask = null;
                    foreach ($tasks as $task) {
                        // 未分配 并且执行人为0
                        if ($task['is_assignee'] == 'no' && $task['executor'] == 0) {
                            $notAssignedStepCategoryTask = $task;
                            $stepTaskIdList = model(TaskRelationModel::class)
                                ->setSuffix(sprintf('_project_%d', $task['project_id']))
                                ->alias('task_relation')
                                ->join('task ON task.id = tr.target_task_id')
                                ->where([
                                    'task_relation.source_task_id' => $notAssignedStepCategoryTask['id'],
                                    'task.executor' => 0,
                                    'task.is_assignee' => 'no',
                                ])
                                ->field('task_relation.id,task_relation.target_task_id,task.project_id')
                                ->select();
                            // 未分配的工序任务列表
                            if (count($stepTaskIdList) > 0) {
                                foreach ($stepTaskIdList as $stepTaskIdListItem) {
                                    $deleteTaskRelationIdList[] = $stepTaskIdListItem;
                                }
                                $deleteTaskIdList = array_merge($deleteTaskIdList, array_column($stepTaskIdList, 'target_task_id'), [$notAssignedStepCategoryTask['id']]);
                            }
                            continue;
                        }
                    }
                }
            }
        }

        if (count($deleteTaskIdList) > 0) {
            $res['deleted_task_id_res'] = model(TaskModel::class)->deleteItem(['id' => ['IN', implode(',', $deleteTaskIdList)]]);
        }
        if (count($deleteTaskRelationIdList) > 0) {
            $delRes = [];
            foreach ($deleteTaskRelationIdList as $deleteTaskRelationItem) {
                $delRes[] = model(TaskRelationModel::class)
                    ->setSuffix(sprintf('_project_%d', $deleteTaskRelationItem['project_id']))
                    ->deleteItem(['id' => $deleteTaskRelationItem['id']]);
            }
            $res['delete_task_relation_id_res'] = $delRes;
        }
        $res['deleted_task_id'] = $deleteTaskIdList;

        $res['delete_task_relation_id'] = $deleteTaskRelationIdList;

        return $res;
    }


    /**
     * 通过下游环节生成上游实体
     * @param $currentModuleCode
     * @param $createEntityModuleCode
     * @param $downstreamEntityId
     * @return void
     * @throws \think\Exception
     */
    private function createEntityByDownstreamEntity($currentModuleCode, $createEntityModuleCode, $downstreamEntityId)
    {
        $createModuleId = Module::$moduleDictData['module_index_by_code'][$createEntityModuleCode]['id'];
        $designCategoryId = model(StepCategoryModel::class)->where(['entity_id' => $createModuleId, 'parent_id' => 0])->cache(C('database.default_query_cache_time'))->getField('id') ?? 0;
        $asset = model(EntityModel::class)->field('id,created,is_generate,is_generated_follow,cost_hours,hour_proportion,estimate_hours,uuid,category_id,is_assignee,module_id', true)->find($downstreamEntityId);
        if (empty($asset)) {
            return;
        }
        $jsonFields = json_decode($asset['json'] ?? '{}', true);
        unset($asset['json']);
        $projectTemplateId = model(ProjectModel::class)->where(['id' => $asset['project_id']])->value('project_template_id');
        $asset['module_id'] = $createModuleId;
        $stepWorkflowId = model(StepWorkflowModel::class)
            ->where([
                'code' => $createEntityModuleCode,
                'project_template_id' => $projectTemplateId,
            ])
            ->cache(C('database.default_query_cache_time'))
            ->getField('id') ?? 0;
        $asset['step_workflow_id'] = $stepWorkflowId;
        $asset['category_id'] = $designCategoryId;
        $asset['is_generated_follow'] = 'yes';
        foreach ($jsonFields as $key => $value) {
            $newKey = str_replace_once($currentModuleCode, $createEntityModuleCode, $key);
            if ($newKey === $createEntityModuleCode . '_design' || $newKey === $createEntityModuleCode . '_status') {
                continue;
            }
            $asset[$newKey] = $value;
        }
        if ($currentModuleCode === 'level') {
            $asset[$createEntityModuleCode . '_category'] = '关卡';
        } else if ($currentModuleCode === 'shot' && $asset['entity_id'] > 0) {
            $sequence = model(EntityModel::class)->find($asset['entity_id']);
            if (!empty($sequence)) {
                $asset[$createEntityModuleCode . '_sequence'] = $sequence['name'];
            }
        }

        $commonService = CommonService::instance('entity');
        $commonService->setCurrentModuleCode($createEntityModuleCode);
        request()->getModuleCode($createEntityModuleCode);
        request()->getTenantId($asset['tenant_id']);
        $asset['plan_start_time'] = get_format_date($asset['plan_start_time'], 1);
        $asset['plan_end_time'] = get_format_date($asset['plan_start_time'], 1);
        $asset['hour_proportion'] = 1;
        $addRes = $commonService->commonCreate(['data' => $asset], Module::$moduleDictData['module_index_by_code'][$createEntityModuleCode], false);
        Client::send('entity_publish', build_queue_data_with_xu(['filter' => ['id' => $addRes['id']], 'module_code' => $createEntityModuleCode, 'tenant_id' => $addRes['tenant_id'], 'user_id' => request()->getUserId()]), 60); // 间隔三十秒进行发布
        model(EntityModel::class)->execute("update entity set json = json_set(IFNULL(json,'{}'),'$.{$currentModuleCode}_{$createEntityModuleCode}',{$addRes['id']}) where id = $downstreamEntityId");
    }

    /**
     * 资产/关卡 对比概念 进行填充概念数据
     * @param $param
     * @param $moduleCode
     * @return mixed
     * @throws \think\Exception
     */
    public function fixAssetPreModuleData($param, $moduleCode)
    {
        /**
         * 资产/关卡 对比概念 进行填充概念数据
         */
        $designFilter = $assetFilter = $param['filter'];
        $assetFilter[] = ['module_id' => Module::$moduleDictData['module_index_by_code'][$moduleCode]['id']];
        $designFilter[] = ['module_id' => Module::$moduleDictData['module_index_by_code']['design']['id']];
        $assetQuerySql = model(EntityModel::class)->where($assetFilter)->buildSql();
        $designQuerySql = model(EntityModel::class)->where($designFilter)->buildSql();
        $result = model(EntityModel::class)->query("SELECT design.id as design_id,asset.id as asset_id,asset.json->>'$.{$moduleCode}_design' as asset_design_relate_id
FROM (
$assetQuerySql as asset 
LEFT JOIN $designQuerySql as design ON asset.name=design.name) 
WHERE (asset.json->>'$.{$moduleCode}_design' is null or asset.json->>'$.{$moduleCode}_design' = '' or asset.json->>'$.asset_design' <> design.id ) and asset.id is not null");
        foreach ($result as $row) {
            if (empty($row['design_id'])) {
                //需要添加概念 并且更新资产/关卡的关联概念id
                $this->createEntityByDownstreamEntity($moduleCode, 'design', $row['asset_id']);
            } else {
                if ($row['design_id'] == $row['asset_design_relate_id']) {
                    continue;
                }
                if (empty($row['asset_design_relate_id'])) {
                    model(EntityModel::class)->execute("update entity set json = JSON_SET(IFNULL(json,'{}'),'$.{$moduleCode}_design',{$row['design_id']}) where id = {$row['asset_id']}");
                    continue;
                }
                // 检查概念存不存在
                $relateDesign = model(EntityModel::class)->find($row['asset_design_relate_id']);
                if (empty($relateDesign)) {
                    // 之前的概念不存在  那么就检查name一样的概念
                    if ($row['design_id'] > 0) {
                        // 如果匹配上概念 就更新概念
                        model(EntityModel::class)->execute("update entity set json = json_set(IFNULL(json,'{}'),'$.{$moduleCode}_design',{$row['design_id']}) where id = {$row['asset_id']}");
                    } else {
                        // 新建概念
                        $this->createEntityByDownstreamEntity($moduleCode, 'design', $row['asset_id']);
                    }
                }
            }
        }
        return $result;
    }

    /**
     * 填充镜头前置模块数据
     * @param $param
     * @return mixed
     * @throws \think\Exception
     */
    public function fixShotPreModuleData($param)
    {
        $storyboardFilter = $shotFilter = $sequenceFilter = $param['filter'];
        $storyboardFilter[] = ['module_id' => Module::$moduleDictData['module_index_by_code']['storyboard']['id']];
        $shotFilter[] = ['module_id' => Module::$moduleDictData['module_index_by_code']['shot']['id']];
        $sequenceFilter[] = ['module_id' => Module::$moduleDictData['module_index_by_code']['sequence']['id']];
        $shotQuerySql = model(EntityModel::class)->where($shotFilter)->buildSql();
        $sequenceQuerySql = model(EntityModel::class)->where($sequenceFilter)->buildSql();
        $storyboardQuerySql = model(EntityModel::class)->where($storyboardFilter)->buildSql();
        $result = model(EntityModel::class)->query("select shot.id as shot_id ,storyboard.id as storyboard_id,shot.name as shot_name,storyboard.name as storyboard_name,sequence.name as sequence_name,storyboard.json->>'$.storyboard_sequence',shot.json->>'$.shot_storyboard' as bind_storyboard_id
from (
      $shotQuerySql as shot 
      LEFT JOIN ($sequenceQuerySql) as sequence ON sequence.id = shot.entity_id
      LEFT JOIN ($storyboardQuerySql) as storyboard ON shot.initial_episode_code=storyboard.initial_episode_code and shot.showings_number = storyboard.showings_number and shot.name = storyboard.name and storyboard.json->>'$.storyboard_sequence' = sequence.name 
) 
WHERE (shot.json->>'$.shot_storyboard' is null or shot.json->>'$.shot_storyboard' ='' or storyboard.id is null or shot.json->>'$.shot_storyboard' <> storyboard.id ) and shot.id is not null
");
        foreach ($result as $row) {
            if (!empty($row['bind_storyboard_id'])) {
                // 不为空 那么检查是否存在
                $storyboard = model(EntityModel::class)->find($row['bind_storyboard_id']);
                if (!empty($storyboard)) {
                    continue;
                } else {
                    $this->createEntityByDownstreamEntity('shot', 'storyboard', $row['shot_id']);
                }
            } else {
                if (!empty($row['storyboard_id'])) {
                    // 匹配上分镜数据就更新镜头的分镜字段
                    model(EntityModel::class)->execute("update entity set json = json_set(IFNULL(json,'{}'),'$.shot_storyboard',{$row['storyboard_id']}) where id = {$row['shot_id']}");
                } else {
                    $this->createEntityByDownstreamEntity('shot', 'storyboard', $row['shot_id']);
                }
            }
        }
        return $result;
    }

    /**
     * 检查逾期实体任务
     * @param $filter
     * @return bool
     */
    public function checkOverdueEntityTask($filter)
    {
        $data['task_filter'] = [
            'step_type' => 'step'
        ];
        if (!empty($filter['is_history'])) {
            $data['public_filter'] = [
                [
                    [
                        'plan_end_time' => ['lt', strtotime(date('Y-m-d H:i:s', time()))]
                    ],
                    '_logic' => 'and',
                    [
                        'plan_end_time' => ['neq', 0],
                    ],
                    'end_time' => ['eq', 0]
                ],
                '_logic' => 'or',
                [
                    [
                        '_string' => 'end_time > plan_end_time'
                    ]
                ]
            ];
        } else {
            if (empty($filter['day'])) {
                return false;
            }
            $data['public_filter'] = [
                [
                    'plan_end_time' => [
                        'BETWEEN',
                        [strtotime(date('Y-m-d', strtotime("-{$filter['day']} day"))), strtotime(date('Y-m-d H:i:s', time()))]
                    ],
                    'end_time' => ['eq', 0]
                ],
                '_logic' => 'or',
                [
                    '_string' => 'end_time > plan_end_time'
                ]
            ];
        }
        $data['task_filter'][] = $data['public_filter'];
        $projectList = model(ProjectModel::class)->column('id');
        $data['module_list'] = model(ModuleModel::class)->where(['code' => ['IN', 'design,shot,asset,level,storyboard']])->select();
        $data['module_code_map'] = array_column($data['module_list'], 'code', 'id');
        $data['upstream_entity'] = ['design', 'storyboard'];
        $data['downstream_entity'] = ['asset', 'level', 'shot'];
        $data['p'] = 0;
        foreach ($projectList as $item) {
            $data['project_item'] = $item;
            Client::send("check_overdue_entity_task", build_queue_data_with_xu($data), 3);
        }
        return true;
    }
}
